#include "JsonCommandParser.h"

#include <cjson/cJSON.h>

#include "Log.h"
#include "ConfigManager.h"
#include "VideoPlayInfo.h"
#include "Utility.h"
#include "Authenticator.h"
#include "StatisticGatherer.h"
#include "CommandParser.h"
#include "IotGatewayInfo.h"


//mpParentCommandDispatcher(pParentCommandDispatcher),mcommandExecutor(this)
JsonCommandParser::JsonCommandParser() {

}

JsonCommandParser::~JsonCommandParser() {
    //dtor

}



void JsonCommandParser::parseCommand(NetworkString& data, NetworkString& result) {
    constexpr NetworkString::size_type startIndex = 3;
    NetworkString::size_type index = data.find('_', startIndex + 1);
    const std::string_view command(&data[startIndex], (NetworkString::npos == index ? data.size() - 1:index) - startIndex);
    auto iterator = mcommandToFunction.find(command);

    if (iterator != mcommandToFunction.end()) {

//       LOG_TO(BizLogHandle, UsedLogger::INFO, "execute command:%s", data.data());

        if (NetworkString::npos != index) {
            ++index;
        }
        result = command;
        (this->*(iterator->second))(data, index, result);
    } else {
        LOG_TO(BizLogHandle, UsedLogger::ERROR, "not found command:%s", data.data());
    }

}


void JsonCommandParser::onExtendDeviceConfig(NetworkString& detailCmd, const NetworkString::size_type dataIndex, std::string& result) {
    ConfigManager& configManager =  ConfigManagerSingleton::instance();

    cJSON* pRoot = cJSON_Parse(&detailCmd.at(dataIndex));
    configManager.parseExtendDeviceCondfig(pRoot);
    cJSON_Delete(pRoot);

    configManager.saveExtendDeviceConfig();
}



std::string& JsonCommandParser::buildAcSwitchStatusResponse(const std::string& switchMode, const std::string& loopSwitchStatus, std::string& result){

    cJSON* pRoot = cJSON_CreateObject();
    cJSON_AddStringToObject(pRoot, "switchMode", switchMode.data());
    cJSON_AddStringToObject(pRoot, "loopSwitchStatus", loopSwitchStatus.data());

    char buffer[STRING_BUFFER_SIZE];
    bool res = static_cast <bool>(cJSON_PrintPreallocated(pRoot, buffer, sizeof (buffer), false));
    cJSON_Delete(pRoot);
    if (res) {
        result.append(1, '_');
        result.append(buffer);
    } else {
        throw BUILD_EXCEPTION_MESSAGE(FirmwareError::JSON_BUILD_ERROR, "cJSON_PrintPreallocated failed");
    }

    return result;
}






void JsonCommandParser::onAcSwitchStatus(NetworkString& detailCmd, const NetworkString::size_type dataIndex, std::string& result) {
    std::string switchMode, loopSwitchStatus;
    mcommandExecutor.onAcSwitchStatus(switchMode, loopSwitchStatus);

    buildAcSwitchStatusResponse(switchMode, loopSwitchStatus, result);
}



std::string& JsonCommandParser::buildAlertInfo(const std::string& alertType, std::string& result){

    cJSON* pRoot = cJSON_CreateObject();
    cJSON_AddStringToObject(pRoot, "type", alertType.data());

    char buffer[STRING_BUFFER_SIZE];
    bool res = static_cast <bool>(cJSON_PrintPreallocated(pRoot, buffer, sizeof (buffer), false));
    cJSON_Delete(pRoot);
    if (res) {
        result.append(1, '_');
        result.append(buffer);
    } else {
        throw BUILD_EXCEPTION_MESSAGE(FirmwareError::JSON_BUILD_ERROR, "cJSON_PrintPreallocated failed");
    }

    return result;
}








void JsonCommandParser::onVideoControl(NetworkString& detailCmd, const NetworkString::size_type dataIndex, std::string& result) {
    cJSON* pRoot = cJSON_Parse(&detailCmd.at(dataIndex));
    cJSON* pEntry = nullptr;
    VideoPlayInfo videoPlayInfo;
    LOAD_CONFIG_FIELD(pRoot, videoPlayInfo, videoId, StringUtility::EMPTY_STRING);
    LOAD_CONFIG_FIELD(pRoot, videoPlayInfo, speed, StringUtility::EMPTY_STRING);
    LOAD_CONFIG_FIELD(pRoot, videoPlayInfo, bright, StringUtility::EMPTY_STRING);
    LOAD_CONFIG_FIELD(pRoot, videoPlayInfo, mode, StringUtility::EMPTY_STRING);
    cJSON_Delete(pRoot);

    mcommandExecutor.onVideoControl(videoPlayInfo, result);
}



void JsonCommandParser::onDownloadFile(NetworkString& detailCmd, const NetworkString::size_type dataIndex, std::string& result) {
    cJSON* pRoot = cJSON_Parse(&detailCmd.at(dataIndex));
    cJSON* pEntry = nullptr;
    UriInfo uriInfo;

    std::string errorMassage;

    pEntry = cJSON_GetObjectItemCaseSensitive(pRoot, "storageHost");
    if (pEntry) {
        uriInfo.ip = pEntry->valuestring;
    } else {
        errorMassage = "not found host";
    }

    pEntry = cJSON_GetObjectItemCaseSensitive(pRoot, "storagePort");
    if (pEntry) {
        uriInfo.port = pEntry->valueint;
    } else {
        errorMassage = "not found port";
    }

    pEntry = cJSON_GetObjectItemCaseSensitive(pRoot, "filePath");
    if (pEntry) {
        uriInfo.url = pEntry->valuestring;
    } else {
        errorMassage = "not found filePath";
    }

    std::string module = LOAD_CONFIG_STRING(pRoot, module, ExtendDeviceEnum::SELF.name);
    std::string opaque = LOAD_CONFIG_STRING(pRoot, opaque, StringUtility::EMPTY_STRING);

    cJSON_Delete(pRoot);

    if (!errorMassage.empty()) {
        throw BUILD_EXCEPTION_MESSAGE(FirmwareError::UPDATE_FIRMWARE_ERROR, errorMassage);
    }

    mcommandExecutor.onDownloadFile(uriInfo, result);
}



void JsonCommandParser::lock(NetworkString& detailCmd, const NetworkString::size_type dataIndex, std::string& result) {
    mcommandExecutor.lock(result);
}

void JsonCommandParser::unlock(NetworkString& detailCmd, const NetworkString::size_type dataIndex, std::string& result) {
    cJSON* pRoot = cJSON_Parse(&detailCmd.at(dataIndex));
    cJSON* pEntry = nullptr;

    const std::time_t timeout = LOAD_CONFIG_INT(pRoot, timeout, 60);
    cJSON_Delete(pRoot);

    mcommandExecutor.unlock(timeout, result);
}


void JsonCommandParser::updateBackupPassword(NetworkString& detailCmd, const NetworkString::size_type dataIndex, std::string& result) {
    cJSON* pRoot = cJSON_Parse(&detailCmd.at(dataIndex));
    cJSON* pEntry = nullptr;

    const std::string password = LOAD_CONFIG_STRING(pRoot, password, StringUtility::EMPTY_STRING);
    cJSON_Delete(pRoot);
    const std::string::size_type size = password.size();
    if (1 > size || 8 < size) {
        throw BUILD_EXCEPTION_MESSAGE(FirmwareError::UPDATE_PASSWORD_ERROR, "password is too short!");
    } else {
        mcommandExecutor.updatePassword(password, result);
    }
}


void JsonCommandParser::onUploadOfflineSystemInfo(NetworkString& detailCmd, const NetworkString::size_type dataIndex, std::string& result) {
    StatisticInfoPersistor& persistor = StatisticGathererSingleton::instance().minfoPersistor;

    int returnCode = 0;
    std::string& initialString = result;
    initialString += '_';
    std::string dataString = initialString;

    if (!persistor.openForRead()){
        throw BUILD_EXCEPTION_MESSAGE(FirmwareError::UPLOAD_OFFLINE_SYSTEM_INFO_ERROR, "read offline system info file failed!");
    }
    while(0 < (returnCode = persistor.readFromFile(dataString))) {
        mcommandExecutor.mparentCommandParser->buildAndSend(detailCmd, dataString, true, true);
        dataString = initialString;
    }
    persistor.closeForRead();
    if (0 > returnCode){
        throw BUILD_EXCEPTION_MESSAGE(FirmwareError::UPLOAD_OFFLINE_SYSTEM_INFO_ERROR, "read offline system info failed!");
    } else {
        result = dataString;
    }

}



void JsonCommandParser::onExtendDeviceStatus(NetworkString& detailCmd, const NetworkString::size_type dataIndex, std::string& result) {
    std::vector<ExtendDeviceInfo> deviceInfos;
    mcommandExecutor.onExtendDeviceStatus(deviceInfos);

    cJSON *pRoot = cJSON_CreateArray(), *pItem = nullptr;

    for (std::vector<ExtendDeviceInfo>::iterator iterator = deviceInfos.begin(); iterator != deviceInfos.end(); ++iterator){
        pItem = cJSON_CreateObject();
        ExtendDeviceInfo& deviceInfo{*iterator};
        SAVE_CONFIG_FIELD(pItem, deviceInfo, name);
        SAVE_CONFIG_FIELD(pItem, deviceInfo, version);

        cJSON_AddItemToArray(pRoot, pItem);
    }

    char buffer[STRING_BUFFER_SIZE << 2];
    bool res = static_cast <bool>(cJSON_PrintPreallocated(pRoot, buffer, sizeof (buffer), false));
    cJSON_Delete(pRoot);
    if (res) {
        result.reserve(sizeof buffer);
        result.append(1, '_');
        result.append(buffer);
    } else {
        throw BUILD_EXCEPTION_MESSAGE(FirmwareError::JSON_BUILD_ERROR, "cJSON_PrintPreallocated failed");
    }

}



void JsonCommandParser::onForwardData(NetworkString& detailCmd, const NetworkString::size_type dataIndex, std::string& result){
    cJSON* pRoot = cJSON_Parse(&detailCmd.at(dataIndex));
    cJSON* pEntry = nullptr;
    VerbInfo verbInfo;
    LOAD_CONFIG_FIELD(pRoot, verbInfo, ip, StringUtility::EMPTY_STRING);
    LOAD_CONFIG_INT_FIELD(pRoot, verbInfo, port, 51000);
    LOAD_CONFIG_INT_FIELD(pRoot, verbInfo, timeout, 100);
    LOAD_CONFIG_INT_FIELD(pRoot, verbInfo, dataIsBinary, false);
    LOAD_CONFIG_FIELD(pRoot, verbInfo, data, StringUtility::EMPTY_STRING);
    LOAD_CONFIG_INT_FIELD(pRoot, verbInfo, haveResponse, false);
    LOAD_CONFIG_INT_FIELD(pRoot, verbInfo, responseIsBinary, false);
    cJSON_Delete(pRoot);

    VerbResult verbResult;
    mcommandExecutor.onForwardData(verbInfo, verbResult);

    pRoot = cJSON_CreateObject();
    SAVE_CONFIG_FIELD(pRoot, verbResult, result);
    SAVE_CONFIG_INT_FIELD(pRoot, verbResult, errorCode);
    SAVE_CONFIG_FIELD(pRoot, verbResult, errorMessage);

    char buffer[STRING_BUFFER_SIZE];
    bool res = static_cast <bool>(cJSON_PrintPreallocated(pRoot, buffer, sizeof (buffer), false));
    cJSON_Delete(pRoot);

    if (res) {
        result.reserve(sizeof buffer);
        result.append(1, '_');
        result.append(buffer);
    } else {
        throw BUILD_EXCEPTION_MESSAGE(FirmwareError::JSON_BUILD_ERROR, "cJSON_PrintPreallocated failed");
    }


}


